package com.mapbased.wfw;

import java.io.*;
import java.util.*;
import javax.servlet.http.*;

import org.apache.commons.fileupload.*;

/**
 * <p>Title: </p>
 *
 * <p>Description: </p>
 *
 * <p>Copyright: Copyright (c) 2004</p>
 *
 * <p>Company: mapbased.com</p>
 *
 * @author chy_hs(Baily)
 * @version 1.0
 */
public class MultiPartParser
{
    public static final String CONTENT_TYPE="Content-type";

    public static void parseMultiPartRequest(RenderContext rc)
        throws IOException
    {
        HttpServletRequest request=rc.getRequest();
        if(!org.apache.commons.fileupload.FileUploadBase.isMultipartContent(
            request))
        {
            return;
        }

        String contentType=request.getHeader(CONTENT_TYPE);
        int boundaryIndex=contentType.indexOf("boundary=");
        if(boundaryIndex < 0)
        {
            throw new IOException("the request was rejected because " +
                                  "no multipart boundary was found");
        }
        byte[] boundary=contentType.substring(boundaryIndex + 9).getBytes();

        InputStream input=request.getInputStream();

        MultipartStream multi=new MultipartStream(input,boundary);
        multi.setHeaderEncoding(request.getCharacterEncoding());

        boolean nextPart=multi.skipPreamble();
        while(nextPart)
        {
            Map headers=parseHeaders(multi.readHeaders());
            String fieldName=getFieldName(headers);
            if(fieldName != null)
            {
                String subContentType=getHeader(headers,CONTENT_TYPE);
                if(subContentType != null &&
                   subContentType.startsWith(MULTIPART_MIXED))
                {
                    // Multiple files.
                    byte[] subBoundary=subContentType.substring(subContentType.
                        indexOf("boundary=") + 9).getBytes();
                    multi.setBoundary(subBoundary);
                    boolean nextSubPart=multi.skipPreamble();
                    while(nextSubPart)
                    {
                        headers=parseHeaders(multi.readHeaders());
                        if(getFileName(headers) != null)
                        {
                            OutputStream os=rc.getPageHandler().onDataUpload(rc,
                                fieldName,headers);
                            if(os != null)
                            {

                                // A single file.

                                try
                                {
                                    multi.readBodyData(os);
                                }
                                finally
                                {
                                    os.close();
                                }
                            }
                            else
                            {
                                multi.discardBodyData();
                            }

                        }
                        else
                        {
                            // Ignore anything but files inside
                            // multipart/mixed.
                            multi.discardBodyData();
                        }
                        nextSubPart=multi.readBoundary();
                    }
                    multi.setBoundary(boundary);
                }
                else
                {
                    if(getFileName(headers) != null)
                    {
                        OutputStream os=rc.getPageHandler().onDataUpload(rc,
                            fieldName,headers);
                        if(os != null)
                        {

                            // A single file.

                            try
                            {
                                multi.readBodyData(os);
                            }
                            finally
                            {
                                os.close();
                            }
                        }
                        else
                        {
                            multi.discardBodyData();
                        }
                    }
                    else
                    {
                        java.io.ByteArrayOutputStream os=new java.io.
                            ByteArrayOutputStream();
                        try
                        {
                            multi.readBodyData(os);
                            String enc=request.getCharacterEncoding();
                            if(enc == null)
                            {
                                enc="utf-8";
                            }
                            String s=os.toString(enc);

                            Object o=rc.getRequestMap().get(fieldName);

                            if(o == null)
                            {
                                rc.getRequestMap().put(fieldName,s);
                            }
                            else if(o instanceof String)
                            {
                                String[] ss=new String[2];
                                ss[0]=(String)o;
                                ss[1]=s;
                                rc.getRequestMap().put(fieldName,ss);
                            }
                            else if(o instanceof String[])
                            {
                                String[] ss=(String[])o;
                                String[] newss=new String[ss.length + 1];
                                System.arraycopy(ss,0,newss,0,ss.length);
                                newss[newss.length - 1]=s;

                                rc.getRequestMap().put(fieldName,newss);
                            }

                        }
                        finally
                        {
                            os.close();
                        }

                    }
                }
            }
            else
            {
                // Skip this part.
                multi.discardBodyData();
            }
            nextPart=multi.readBoundary();
        }

    }

    /**
     * <p> Parses the <code>header-part</code> and returns as key/value
     * pairs.
     *
     * <p> If there are multiple headers of the same names, the name
     * will map to a comma-separated list containing the values.
     *
     * @param headerPart The <code>header-part</code> of the current
     *                   <code>encapsulation</code>.
     *
     * @return A <code>Map</code> containing the parsed HTTP request headers.
     */

    public static final int MAX_HEADER_SIZE=1024;
   static protected Map /* String, String */ parseHeaders(String headerPart)
    {
        Map headers=new HashMap();
        char buffer[]=new char[MAX_HEADER_SIZE];
        boolean done=false;
        int j=0;
        int i;
        String header,headerName,headerValue;
        try
        {
            while(!done)
            {
                i=0;
                // Copy a single line of characters into the buffer,
                // omitting trailing CRLF.
                while(i < 2 || buffer[i - 2] != '\r' || buffer[i - 1] != '\n')
                {
                    buffer[i++]=headerPart.charAt(j++);
                }
                header=new String(buffer,0,i - 2);
                if(header.equals(""))
                {
                    done=true;
                }
                else
                {
                    if(header.indexOf(':') == -1)
                    {
                        // This header line is malformed, skip it.
                        continue;
                    }
                    headerName=header.substring(0,header.indexOf(':')).trim().
                        toLowerCase();
                    headerValue=header.substring(header.indexOf(':') + 1).trim();
                    if(getHeader(headers,headerName) != null)
                    {
                        // More that one heder of that name exists,
                        // append to the list.
                        headers.put(headerName,
                                    getHeader(headers,headerName) + ',' +
                                    headerValue);
                    }
                    else
                    {
                        headers.put(headerName,headerValue);
                    }
                }
            }
        }
        catch(IndexOutOfBoundsException e)
        {
            // Headers were malformed. continue with all that was
            // parsed.
        }
        return headers;
    }

    private static final String getHeader(Map /* String, String */ headers,
                                          String name)
    {
        return(String)headers.get(name.toLowerCase());
    }

    static private String getFileName(Map /* String, String */ headers)
    {
        String fileName=null;
        String cd=getHeader(headers,CONTENT_DISPOSITION);
        if(cd.startsWith(FORM_DATA) || cd.startsWith(ATTACHMENT))
        {
            int start=cd.indexOf("filename=\"");
            int end=cd.indexOf('"',start + 10);
            if(start != -1 && end != -1)
            {
                fileName=cd.substring(start + 10,end).trim();
            }
        }
        return fileName;
    }

    /**
     * Retrieves the field name from the <code>Content-disposition</code>
     * header.
     *
     * @param headers A <code>Map</code> containing the HTTP request headers.
     *
     * @return The field name for the current <code>encapsulation</code>.
     */
    public static final String CONTENT_DISPOSITION="Content-disposition";
    public static final String ATTACHMENT="attachment";
    public static final String MULTIPART_MIXED="multipart/mixed";

    /**
     * Content-disposition value for form data.
     */
    public static final String FORM_DATA="form-data";

    private static String getFieldName(Map /* String, String */ headers)
    {
        String fieldName=null;
        String cd=getHeader(headers,CONTENT_DISPOSITION);
        if(cd != null && cd.startsWith(FORM_DATA))
        {
            int start=cd.indexOf("name=\"");
            int end=cd.indexOf('"',start + 6);
            if(start != -1 && end != -1)
            {
                fieldName=cd.substring(start + 6,end);
            }
        }
        return fieldName;
    }

}
